#!/usr/bin/env python3

# Convert png icons to bitmap.hpp inspired by
#  make_bitmap.py - Copyright (C) 2016 Furrtek
# Convert bitmap.hpp to icons inspired by
#  bitmap_arr_reverse_decode.py - Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
#  bitmap_arr_reverse_decode.py - copyleft 2024 zxkmm AKA zix aka sommermorgentraum
# Copysomething (c) 2024 LupusE with the license, needed by the PortaPack project
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING.  If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#

import argparse
import numpy as np
import re
import sys
import os
from PIL import Image

### Convert a directory of icons in png format to one bitmap.hpp file.
######################################################################

def pp_bitmaphpp_header():
    return ("""/*
 * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
 * Copyright (C) 2016 Furrtek
 *
 * This file is part of PortaPack.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

// This file was generated by make_bitmap.py

#ifndef __BITMAP_HPP__
#define __BITMAP_HPP__

#include \"ui.hpp\"

namespace ui {
""")


def convert_png(file):
	bmp_data = []
	data = 0

	rgb_im = Image.open(file).convert('RGBA')

	if rgb_im.size[0] % 8 or rgb_im.size[1] % 8:
		print((file + ": Size is not a multiple of 8. Image is not included in bitmap.hpp."))
		sys.exit(-1)

	name = os.path.basename(file).split(".")[0].lower()

	bmp_data.append(f"\nstatic constexpr uint8_t bitmap_{name}_data[] = {{\n")

	for i in range(rgb_im.size[1]):
		for j in range(rgb_im.size[0]):
			r, g, b, a = rgb_im.getpixel((j, i))
			
			data >>= 1
			
			if r > 127 and g > 127 and b > 127 and a > 127:
				data += 128
			
			if j % 8 == 7:
				bmp_data.append("    0x%0.2X,\n" % data)
				data = 0

	bmp_data.append(f"""}};
static constexpr Bitmap bitmap_{name}{{
    {{{str(rgb_im.size[0])}, {str(rgb_im.size[1])}}},
    bitmap_{name}_data}};
""")
	
	out_bmpdata = ''.join(map(str, bmp_data))
	
	return (out_bmpdata)

def pp_bitmaphpp_data(pngicons_path):
    count = 0
    bitmaphpp_data = []
    for file in os.listdir(pngicons_path):
        if os.path.isfile(pngicons_path + file) and file.endswith(".png"):
            bitmaphpp_data.append(convert_png(pngicons_path + file))
            count += 1

    return bitmaphpp_data
    

def pp_bitmaphpp_footer():
    return("""
} /* namespace ui */
	
#endif /*__BITMAP_HPP__*/
""")


def pp_write_bitmaphpp(pngicons_path, hpp_outpath):
    bitmaphpp_file = []

    bitmaphpp_file.append(pp_bitmaphpp_header())
    bitmaphpp_file.append("".join(str(x) for x in pp_bitmaphpp_data(pngicons_path)))
    bitmaphpp_file.append(pp_bitmaphpp_footer())
    
    out_file = "".join(str(x) for x in bitmaphpp_file)

    with open(hpp_outpath, "w", encoding="utf-8") as fd:
        fd.writelines(out_file)
    
    print("Find your bitmap.hpp at", hpp_outpath)




### Convert from a bitmap.hpp file one or all icons in png.
###########################################################

def parse_bitmaphpp(bitmaphpp_file,icon_name):
    ico_pattern = re.compile(r"static constexpr uint8_t bitmap_(.*)_data\[\] = {\n((?:\s+(?:.*)\n)+)};\nstatic constexpr Bitmap bitmap_.*\{\n\s+\{(.*)\},", re.MULTILINE)
    ico_data = []
    
    # read file to buffer, to find multiline regex
    readfile = open(bitmaphpp_file,'r')
    buff = readfile.read()
    readfile.close()

    if icon_name == 'all':
        for match in ico_pattern.finditer(buff):
            ico_data.append([match.group(1), match.group(2), match.group(3)])
    else:
        for match in ico_pattern.finditer(buff):
            if match.group(1) in icon_name:
                ico_data.append([match.group(1), match.group(2), match.group(3)])
        
    return (ico_data)


def convert_hpp(icon_name,bitmap_array,iconsize_str,png_outdir):
    iconsize = iconsize_str.split(", ")
    bitmap_size = (int(iconsize[0]),int(iconsize[1]))
    bitmap_data=[]

    image_data = np.zeros((bitmap_size[1], bitmap_size[0]), dtype=np.uint8)

    for value in bitmap_array.split(",\n"):
        if (value):
            bitmap_data.append(int(value, 0))

    for y in range(bitmap_size[1]):
        for x in range(bitmap_size[0]):
            byte_index = (y * bitmap_size[0] + x) // 8
            bit_index = x % 8
            # bit_index = 7 - (x % 8)
            pixel_value = (bitmap_data[byte_index] >> bit_index) & 1
            image_data[y, x] = pixel_value * 255

    image = Image.fromarray(image_data, 'L')
    icon_out = os.path.join(png_outdir,icon_name+".png")
    image.save(icon_out)


### Processing
##############

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("hpp", help="Path of the bitmap.hpp")
    parser.add_argument("graphics", help="Path of <icon>.png files to convert", default=os.path.join(os.getcwd(),"graphics"))
    parser.add_argument("--icon","-i", help="Name of the icon to convert in reverse. Use 'all' to convert all. More names can be comma seperated.")
    parser.add_argument("--reverse","-r", help="Convert icon from bitmap.hpp to <name>.png", action="store_true")
    
    args = parser.parse_args()

    if args.reverse:
        print("Reverse: Converting from hpp to png")
                
        # filter hpp arg is a file
        hpp_file_path = args.hpp
        if not os.path.isfile(hpp_file_path):
            print(f"Error: {hpp_file_path} is not a valid file.")
            sys.exit(1)
        
        # filter graph arg is a path
        if args.graphics:
            graphics_path = os.path.join(args.graphics, '')
        else:
            graphics_path = os.path.join(os.getcwd(),"graphics", '')
        if not os.path.exists(graphics_path):
             os.makedirs(graphics_path)  # create if not exist

        # define icons to convert
        if args.icon:
            icon_name = args.icon
        else:
            icon_name = 'titlebar_image'

        icons = parse_bitmaphpp(hpp_file_path, icon_name)

        for icon in icons:
            print("Converting icon", icon[0])
            convert_hpp(icon[0], icon[1], icon[2], graphics_path)
        sys.exit()
    else:
        print("Converting from png to hpp")
        if args.graphics:
            graphics_path = os.path.join(args.graphics, '')
        else:
            graphics_path = os.path.join(os.getcwd(),"graphics", '')
        print("From path", graphics_path, "to file", args.hpp)
        pp_write_bitmaphpp(graphics_path, args.hpp)
        sys.exit()
